﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics.Contracts;

namespace YasharEl.Infrastructure.PresentationModel.Threading
{
    /// <summary>
    /// Singleton class providing the default implementation 
    /// for the <see cref="ISynchronizationContext"/>, specifically for the UI thread.
    /// </summary>
    public partial class UISynchronizationContext : ISynchronizationContext
    {
        #region Fields

        readonly object initializationLock = new object();
        DispatcherSynchronizationContext context;
        Dispatcher dispatcher;

        #endregion

        #region Singleton implementation

        static readonly UISynchronizationContext instance = new UISynchronizationContext();

        /// <summary>
        /// Gets the singleton instance.
        /// </summary>
        /// <value>The singleton instance.</value>
        public static ISynchronizationContext Instance
        {
            get
            {
                return instance;
            }
        }

        UISynchronizationContext()
        {
            /* Intentionally left blank. */
        }

        #endregion

        #region Public Methods

        public void Initialize()
        {
            EnsureInitialized();
        }

        #endregion

        #region Private Methods

        void EnsureInitialized()
        {
            if (dispatcher != null && context != null)
            {
                return;
            }

            lock (initializationLock)
            {
                if (dispatcher != null && context != null)
                {
                    return;
                }

                try
                {
                    dispatcher = Dispatcher.CurrentDispatcher;
                    context = new DispatcherSynchronizationContext(dispatcher);
                }
                catch (InvalidOperationException)
                {
                    throw new ConcurrencyException("Initialised called from non-UI thread.");
                }
            }
        }

        #endregion

        #region ISynchronizationContext Implementations

        [EditorBrowsable(EditorBrowsableState.Never)]
        public void Initialize(Dispatcher dispatcher)
        {
            lock (initializationLock)
            {
                this.dispatcher = dispatcher;
                context = new DispatcherSynchronizationContext(dispatcher);
            }
        }

        public void InvokeWithoutBlocking(SendOrPostCallback callback, object state)
        {
            EnsureInitialized();

            context.Post(callback, state);
        }

        public void InvokeWithoutBlocking(Action action)
        {
            EnsureInitialized();

            dispatcher.BeginInvoke(action);
        }

        public void InvokeAndBlockUntilCompletion(SendOrPostCallback callback, object state)
        {
            EnsureInitialized();

            context.Send(callback, state);
        }

        public void InvokeAndBlockUntilCompletion(Action action)
        {
            
            EnsureInitialized();

            if (dispatcher.CheckAccess())
            {
                action();
            }
            else
            {
                context.Send(delegate { action(); }, null);
            }
        }

        public bool InvokeRequired
        {
            get
            {
                EnsureInitialized();
                return !dispatcher.CheckAccess();
            }
        }

        #endregion
    }
}
